#include "stdafx.h"

//#include <atlbase.h>
#include <oledbdm.h>

#include "DMAlgorithm.h"
#include "DMAlgorithmNavigation.h"
#include "DMAttributeSet.h"
using namespace PluginAlgorithms;

AlgorithmNavigationBase::AlgorithmNavigationBase()
{
}


HRESULT AlgorithmNavigationBase:: ExportedMoveToNextTree()
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		if( !MoveToNextTree() )
			return S_FALSE;
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetNodeID(
		DM_DAGNodeID*	out_pNodeID)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pNodeID =	GetCurrentNodeId();
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase::	ExportedLocateNode(
		DM_DAGNodeID			in_NodeID)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		if( !LocateNode(in_NodeID) )
			return S_FALSE;

	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedValidateNodeID(
		DM_DAGNodeID			in_NodeID)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		if( !ValidateNodeId(in_NodeID) )
			return S_FALSE;
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetParentCount(
		ULONG *				out_pulParents)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pulParents	=	GetParentCount();
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedMoveToParent(
		ULONG					in_ParentIndex)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		MoveToParent(in_ParentIndex);
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetParentNodeID	(
		ULONG					in_ParentIndex,
		DM_DAGNodeID *		out_pNodeID)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pNodeID	=	GetParentNodeId(in_ParentIndex);
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetChildCount(
		ULONG *				out_pulChilds)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pulChilds	=	GetChildrenCount();
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedMoveToChild	(
		ULONG					in_ChildIndex)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		MoveToChild(in_ChildIndex);
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetChildNodeID(
		ULONG					in_ChildIndex,
		DM_DAGNodeID *		out_pNodeID)
{
	USES_ERROR_HANDLING;
	DM_TRY
	{
		*out_pNodeID	=	GetChildNodeId(in_ChildIndex);
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedMoveToNextLeaf(
		DM_DAGNodeID			in_ScopeNodeID)
{
		return E_NOTIMPL;
}



HRESULT AlgorithmNavigationBase::	ExportedAddRefNodeID(
		DM_DAGNodeID			in_NodeID)
{
	return S_OK;
}



HRESULT AlgorithmNavigationBase::	ExportedReleaseNodeID(
		DM_DAGNodeID			in_NodeID)
{
	return S_OK;
}




HRESULT AlgorithmNavigationBase:: ExportedGetNodeProperty(
		IDMContextServices*	in_pContext, 
		DM_NODE_PROP			in_PropID, 
		DMVariantPtr			io_pValue)
{
	AutoPushContext autoCtx(in_pContext);

	VARIANT var;
	::VariantInit( &var );

	USES_ERROR_HANDLING;
	DM_TRY
	{
		switch( in_PropID )
		{
			case DMNP_TYPE:
				{
					NodeType	type	=	GetNodeType();
					V_VT(&var)	=	VT_I4;
					V_I4(&var)	=	(System::Int32)type;
				}
				break;
			case DMNP_CONDITION_XML:
			case DMNP_RULE_XML:
			case DMNP_CAPTION:
			case DMNP_MODEL_COLUMN_NAME:
			case DMNP_DESCRIPTION:
			case DMNP_SHORT_CAPTION:
				{
					String ^	strValue	=	GetStringNodeProperty( (NodeProperty)in_PropID );
					V_VT(&var)	=	VT_BSTR;
					if( strValue )
					{
						//pin_ptr<const wchar_t> pinchars = PtrToStringChars(strValue); 
						V_BSTR(&var)	=	(BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( strValue );
					}
					else
					{
						V_BSTR(&var)	=	::SysAllocString(L"");
					}

				}
				break;
			case DMNP_PROBABILITY:
			case DMNP_MARGINAL_PROBABILITY:
			case DMNP_SUPPORT:
			case DMNP_SCORE:
				{
					System::Double	dValue	=	GetDoubleNodeProperty( (NodeProperty)in_PropID );
					V_VT(&var)	=	VT_R8;
					V_R8(&var)	=	dValue;
				}
				break;
			default:
				return S_FALSE;
		};

		CComPtr<IDMVariantPtrHandler> spVarHandler;
		THROW_ON_FAILURE ( in_pContext->GetVariantHandler( &spVarHandler ) );
		THROW_ON_FAILURE( spVarHandler->CopyVariantToHandle( io_pValue, &var) );
	}
	DM_CATCHALL
	DM_FINALLY
	{
		::VariantClear(&var);
	}

	REPORT_STATUS;

}


// retrieves the array property, Return S_FALSE if property not supported
HRESULT AlgorithmNavigationBase:: ExportedGetNodeArrayProperty		(
		IDMContextServices*	in_pContext, 
		DM_NODE_PROP			in_PropID, 
		ULONG *				out_pulArraySize,
		void **				out_prgArrayBuffer)
{
	AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
		*out_pulArraySize	=	0;
		*out_prgArrayBuffer	=	NULL;


		switch( in_PropID )
		{
			case DMNP_ATTRIBUTES:
				THROW_ON_FAILURE( InternalGetNodeAttributes( out_pulArraySize, out_prgArrayBuffer) );
				break;
			case DMNP_DISTRIBUTION:
				THROW_ON_FAILURE( InternalGetNodeDistribution( out_pulArraySize, out_prgArrayBuffer) );
				break;
			default:
				THROW_ON_FAILURE(E_NOTIMPL);
		}
	}
	DM_CATCHALL;
			
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetNodeUniqueName			(
		IDMContextServices*	in_pContext, 
		DMString *			io_pstrUniqueName)
{
	AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
		String ^	strName	=	GetNodeUniqueName();
		CComPtr<IDMStringHandler>	spStringHandler;


		BSTR bstrTmp = NULL;
		int	length	=	0;
		
		if( strName != nullptr )
		{
			bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( strName );
			length	=	strName->Length;
		}

		
		THROW_ON_FAILURE( in_pContext->GetStringHandler(&spStringHandler) );
		THROW_ON_FAILURE( spStringHandler->CopyBufferToHandle( io_pstrUniqueName, bstrTmp, length) );

		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
		
	}
	DM_CATCHALL;
	
	REPORT_STATUS;
}



HRESULT AlgorithmNavigationBase:: ExportedGetNodeIDFromUniqueName		(
		IDMContextServices*	in_pContext, 
		DMString *			in_pstrUniqueName,
		DM_DAGNodeID *		out_pNodeID)
{
	AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
		CComPtr<IDMStringHandler>	spStringHandler;
		THROW_ON_FAILURE( in_pContext->GetStringHandler(&spStringHandler) );

		LPCWSTR	szName	=	NULL;
		int		length	=	0;
		THROW_ON_FAILURE( spStringHandler->GetConstStringFromHandle( in_pstrUniqueName, &szName, (UINT*)&length) );

		String ^	strName	=	gcnew System::String( szName, 0, length);
		*out_pNodeID	=	GetNodeIdFromUniqueName( strName );
	}
	DM_CATCHALL;

	REPORT_STATUS;

}




HRESULT AlgorithmNavigationBase:: ExportedGetUniqueNameFromNodeID		(
		IDMContextServices*	in_pContext, 
		DM_DAGNodeID 			in_NodeID,
		DMString *			out_pstrUniqueName)
{
	AutoPushContext autoCtx(in_pContext);
	USES_ERROR_HANDLING;

	DM_TRY
	{
		String ^	strName	=	GetUniqueNameFromNodeId(in_NodeID);
		CComPtr<IDMStringHandler>	spStringHandler;

		BSTR bstrTmp = NULL;
		int	length	=	0;
		
		if( strName != nullptr )
		{
			bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( strName );
			length	=	strName->Length;
		}

		
		THROW_ON_FAILURE( in_pContext->GetStringHandler(&spStringHandler) );
		THROW_ON_FAILURE( spStringHandler->CopyBufferToHandle( out_pstrUniqueName, bstrTmp, length) );
		System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
		
	}
	DM_CATCHALL;

	REPORT_STATUS;
}

HRESULT	AlgorithmNavigationBase::ExportedInterfaceSupportsErrorInfo(REFIID riid)
{
	static const IID* arr[] = 
	{
		&__uuidof(IDMDAGNavigation),
		&__uuidof(IDMAlgorithmNavigation)
	};

	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		if (InlineIsEqualGUID(*arr[i],riid))
			return S_OK;
	}
	return S_FALSE;
}

// Internal methods
HRESULT	AlgorithmNavigationBase:: InternalGetNodeAttributes(
		ULONG *				out_pulArraySize,
		void **				out_prgArrayBuffer)
{
	AutoUseContext	apEC;
	USES_ERROR_HANDLING;

	*out_pulArraySize	=	0;
	*out_prgArrayBuffer	=	NULL;

	CComPtr<IDMMemoryAllocator> spAlloc;
	RETURN_ON_FAILURE(apEC->GetVectorAlloc(&spAlloc));

	CComPtr<IDMVariantPtrHandler>	spVarHandler;
	RETURN_ON_FAILURE(apEC->GetVariantHandler(&spVarHandler));


	DMVariantPtr*	rgVars	=	NULL;
	DM_TRY
	{
		array<System::UInt32>^ rgAttributes = nullptr;
		rgAttributes	=	GetNodeAttributes();

		if( rgAttributes == nullptr || rgAttributes->Length ==  0 )
		{
			return S_FALSE;
		}
		
		*out_pulArraySize	=	rgAttributes->Length;

		// Allocate the array of variants
		
		THROW_ON_FAILURE( spAlloc->Alloc( *out_pulArraySize*sizeof(DMVariantPtr), (BYTE**)&rgVars	));
		memset( rgVars, 0, *out_pulArraySize*sizeof(DMVariantPtr));


		for( ULONG	nIndex	=	0; nIndex < *out_pulArraySize; nIndex ++)
		{
			DMVariantPtr	varHandle = NULL;
			THROW_ON_FAILURE( spVarHandler->CreateNewVariantHandle( &varHandle) );
			rgVars[nIndex] = varHandle;
			
			VARIANT			varTmp;
			::VariantInit( &varTmp );
			V_VT(&varTmp)			=	VT_I4;
			V_I4(&varTmp)			=	rgAttributes[nIndex];

			spVarHandler->CopyVariantToHandle( rgVars[nIndex], &varTmp );
			::VariantClear(&varTmp);

		}
		*out_prgArrayBuffer = (void*) rgVars;
		rgVars = NULL;
	}
	DM_CATCH_EX(System::Exception^ ex)
	{
		DM_HANDLE_EXCEPTION(ex);
		// Cleanup
		if( rgVars != NULL )
		{
			for( ULONG	nIndex	=	0; nIndex < *out_pulArraySize; nIndex ++)
			{
				if( rgVars[nIndex] != NULL )
				{
					spVarHandler->DestroyVariantHandle(rgVars[nIndex]);
				}
			}
			spAlloc->Free((BYTE*)rgVars);
			*out_pulArraySize	=	0;
			*out_prgArrayBuffer	=	NULL;
		}
	}

	REPORT_STATUS;
}


HRESULT	AlgorithmNavigationBase:: InternalGetNodeDistribution(
		ULONG *				out_pulArraySize,
		void **				out_prgArrayBuffer)
{
	AutoUseContext	apEC;
	USES_ERROR_HANDLING;

	*out_pulArraySize	=	0;
	*out_prgArrayBuffer	=	NULL;

	CComPtr<IDMMemoryAllocator> spAlloc;
	CComPtr<IDMStringHandler>	spStringHandler;
	
	DM_ATTRIBUTE_STAT*	rgStats	=	NULL;
	DM_TRY
	{
		THROW_ON_FAILURE(apEC->GetVectorAlloc(&spAlloc));

		array<AttributeStatistics ^	 >^ mrgAttStats;
		mrgAttStats	=	GetNodeDistribution();

		if( mrgAttStats == nullptr)
		{
			return S_FALSE;
		}
	
		THROW_ON_FAILURE( apEC->GetStringHandler(&spStringHandler) );

		*out_pulArraySize	=	mrgAttStats->Length;
		THROW_ON_FAILURE( spAlloc->Alloc( *out_pulArraySize*sizeof(DM_ATTRIBUTE_STAT), (BYTE**)&rgStats) );
		memset( rgStats, 0, *out_pulArraySize*sizeof(DM_ATTRIBUTE_STAT));

		for( ULONG nIndex	=	0; nIndex < *out_pulArraySize; nIndex ++ )
		{
			AttributeStatistics ^	pAtt	=	mrgAttStats[nIndex];
			pin_ptr<DM_ATTRIBUTE_STAT> pSrcAttStat	=	&pAtt->m_attStat;

			DM_ATTRIBUTE_STAT& AttStat	=	rgStats[nIndex];
			memcpy( &AttStat, pSrcAttStat, sizeof(DM_ATTRIBUTE_STAT) );
			
			AttStat.ulStates		=	pAtt->StateStatistics->Count;
			AttStat.rgStateStats	=	NULL;

			if( pAtt->NodeId != nullptr && pAtt->NodeId->Length > 0 )
			{
				THROW_ON_FAILURE( spStringHandler->CreateNewStringHandle( &AttStat.strNodeID ) );
				BSTR bstrTmp = (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR( pAtt->NodeId );
				
				CURRENT_STATUS = spStringHandler->CopyBufferToHandle( AttStat.strNodeID, bstrTmp, pAtt->NodeId->Length);
				System::Runtime::InteropServices::Marshal::FreeBSTR( (IntPtr)(void*)bstrTmp );
				THROW_ON_FAILURE( CURRENT_STATUS );
			}

			THROW_ON_FAILURE( spAlloc->Alloc( AttStat.ulStates*sizeof(DM_STATE_STAT), (BYTE**)&AttStat.rgStateStats) );
			memset( AttStat.rgStateStats, 0, AttStat.ulStates*sizeof(DM_STATE_STAT));

			for( ULONG nStateIndex = 0; nStateIndex < AttStat.ulStates; nStateIndex ++)
			{
				StateStatistics^ mState	=	(StateStatistics^)pAtt->StateStatistics[nStateIndex];
				DM_STATE_STAT& stateStat	=	AttStat.rgStateStats[nStateIndex];
				pin_ptr<DM_STATE_STAT> pSrcStateStat	=	&mState->m_StateStat;

				mState->CloneStateStat( &stateStat);

				//memcpy( &stateStat, pSrcStateStat, sizeof(DM_STATE_STAT) );
			}
		}

		*out_prgArrayBuffer = rgStats;
		rgStats = NULL;

	}
	DM_CATCH_EX(System::Exception^ ex)
	{
		DM_HANDLE_EXCEPTION(ex);
		// Cleanup
		if( rgStats != NULL )
		{
			for( ULONG	nIndex	=	0; nIndex < *out_pulArraySize; nIndex ++)
			{
				DM_ATTRIBUTE_STAT& AttStat	=	rgStats[nIndex];
				if( AttStat.rgStateStats != NULL )
					spAlloc->Free((BYTE*)AttStat.rgStateStats);
				if( AttStat.strNodeID != NULL )
				{
					THROW_ON_FAILURE(  spStringHandler->DestroyStringHandle( AttStat.strNodeID ) );
					AttStat.strNodeID = NULL;
				}

			}
			spAlloc->Free((BYTE*)rgStats);
		}
		*out_pulArraySize	=	0;
		*out_prgArrayBuffer	=	NULL;
	}

	REPORT_STATUS;
}
